
#include "platform/opengl/opengl_vertex_array.hpp"

#include "xen/renderer/buffer.hpp"
#include "xen/renderer/vertex_array.hpp"

namespace Xen {

GLenum OpenGLVertexArray::ShaderDataType2OpenGLType(ShaderDataType type) {
    switch (type) {
        case ShaderDataType::Float:     return GL_FLOAT;
        case ShaderDataType::Float2:    return GL_FLOAT;
        case ShaderDataType::Float3:    return GL_FLOAT;
        case ShaderDataType::Float4:    return GL_FLOAT;
        case ShaderDataType::Mat3:      return GL_FLOAT;
        case ShaderDataType::Mat4:      return GL_FLOAT;
        case ShaderDataType::Int:       return GL_INT;
        case ShaderDataType::Int2:      return GL_INT;
        case ShaderDataType::Int3:      return GL_INT;
        case ShaderDataType::Int4:      return GL_INT;
        case ShaderDataType::Bool:      return GL_BOOL;
    }
    XEN_CORE_ASSERT(false, "Unknown shader data type to OpenGL type");
    return GL_NONE;
}

OpenGLVertexArray::OpenGLVertexArray() {
    GLCall(glGenVertexArrays(1, &vao_));
}

OpenGLVertexArray::~OpenGLVertexArray() {
    GLCall(glDeleteVertexArrays(1, &vao_));
}

void OpenGLVertexArray::SetVertexBuffer(const Ref<VertexBuffer>& buffer) {
    XEN_CORE_ASSERT(buffer->GetLayout().GetElements().size(), "vertex buffer has no layout");

    GLCall(glBindVertexArray(vao_));
    buffer->Bind();

    uint32_t idx = 0;
    for (auto it = buffer->GetLayout().cbegin(); it != buffer->GetLayout().cend(); it++) {
        GLCall(glVertexAttribPointer(
                idx,
                it->GetComponentCount(),
                ShaderDataType2OpenGLType(it->type),
                it->normalized ? GL_TRUE : GL_FALSE,
                buffer->GetLayout().GetStride(),
                (const void*)it->offset
        ));
        GLCall(glEnableVertexAttribArray(idx));
        idx++;
    }
    vertex_buffer_ = buffer;
}

void OpenGLVertexArray::SetIndexBuffer(const Ref<IndexBuffer>& buffer) {
    index_buffer_ = buffer;
}

void Xen::OpenGLVertexArray::Bind() {
    GLCall(glBindVertexArray(vao_));
    vertex_buffer_->Bind();
    if (index_buffer_) {
        index_buffer_->Bind();
    }
}

void Xen::OpenGLVertexArray::Unbind() {
    GLCall(glBindVertexArray(0));
    vertex_buffer_->Unbind();
    index_buffer_->Unbind();
}

}
